/*     */ package com.zimbra.cs.mailbox;
/*     */ 
/*     */ import com.google.common.annotations.VisibleForTesting;
/*     */ import com.zimbra.common.localconfig.DebugConfig;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.mailbox.lock.DebugZLock;
/*     */ import com.zimbra.cs.mailbox.lock.ZLock;
/*     */ import com.zimbra.cs.util.Zimbra;
/*     */ import com.zimbra.cs.zookeeper.CuratorManager;
/*     */ import java.util.EmptyStackException;
/*     */ import java.util.Stack;
/*     */ import java.util.concurrent.TimeUnit;
/*     */ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
/*     */ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
/*     */ import org.apache.curator.framework.recipes.locks.InterProcessSemaphoreMutex;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public final class MailboxLock
/*     */ {
/*  45 */   private final ZLock zLock = DebugConfig.debugMailboxLock ? new DebugZLock() : new ZLock();
/*  46 */   private InterProcessSemaphoreMutex dLock = null;
/*  47 */   private final Stack<Boolean> lockStack = new Stack();
/*     */   private Mailbox mbox;
/*     */   
/*     */   public MailboxLock(String id, Mailbox mbox) {
/*  51 */     if (Zimbra.isAlwaysOn()) {
/*     */       try {
/*  53 */         this.dLock = CuratorManager.getInstance().createLock(id);
/*     */       } catch (ServiceException se) {
/*  55 */         ZimbraLog.mailbox.error("could not initialize distributed lock", se);
/*     */       }
/*     */     }
/*  58 */     this.mbox = mbox;
/*     */   }
/*     */   
/*     */   private void acquireDistributedLock(boolean write) throws ServiceException
/*     */   {
/*  63 */     if ((this.dLock != null) && (getHoldCount() == 1)) {
/*     */       try {
/*  65 */         this.dLock.acquire(LC.zimbra_mailbox_lock_timeout.intValue(), TimeUnit.SECONDS);
/*     */       } catch (Exception e) {
/*  67 */         throw new LockFailedException("could not acquire distributed lock", e, null);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void releaseDistributedLock(boolean write)
/*     */   {
/*  74 */     if ((this.dLock != null) && (getHoldCount() == 1)) {
/*     */       try {
/*  76 */         this.dLock.release();
/*     */       } catch (Exception e) {
/*  78 */         ZimbraLog.mailbox.warn("error while releasing distributed lock", e);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   int getHoldCount() {
/*  84 */     return this.zLock.getReadHoldCount() + this.zLock.getWriteHoldCount();
/*     */   }
/*     */   
/*     */   public boolean isWriteLockedByCurrentThread() {
/*  88 */     return this.zLock.isWriteLockedByCurrentThread();
/*     */   }
/*     */   
/*     */   public boolean isUnlocked() {
/*  92 */     return (!isWriteLockedByCurrentThread()) && (this.zLock.getReadHoldCount() == 0);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public void lock()
/*     */   {
/* 101 */     lock(true);
/*     */   }
/*     */   
/*     */   private boolean tryLock(boolean write) throws InterruptedException {
/* 105 */     if (write) {
/* 106 */       return this.zLock.writeLock().tryLock(0L, TimeUnit.SECONDS);
/*     */     }
/* 108 */     return this.zLock.readLock().tryLock(0L, TimeUnit.SECONDS);
/*     */   }
/*     */   
/*     */   private boolean tryLockWithTimeout(boolean write) throws InterruptedException
/*     */   {
/* 113 */     if (write) {
/* 114 */       return this.zLock.writeLock().tryLock(LC.zimbra_mailbox_lock_timeout.intValue(), TimeUnit.SECONDS);
/*     */     }
/* 116 */     return this.zLock.readLock().tryLock(LC.zimbra_mailbox_lock_timeout.intValue(), TimeUnit.SECONDS);
/*     */   }
/*     */   
/*     */ 
/* 120 */   private ThreadLocal<Boolean> assertReadLocks = null;
/*     */   
/*     */ 
/*     */   private synchronized boolean neverReadBeforeWrite(boolean write)
/*     */   {
/* 125 */     if (this.assertReadLocks == null) {
/* 126 */       this.assertReadLocks = new ThreadLocal();
/*     */     }
/* 128 */     if (this.zLock.getWriteHoldCount() == 0) {
/* 129 */       if (write) {
/* 130 */         Boolean readLock = (Boolean)this.assertReadLocks.get();
/* 131 */         if (readLock != null) {
/* 132 */           ZimbraLog.mailbox.error("read lock held before write", new Exception());
/* 133 */           if (!$assertionsDisabled) throw new AssertionError();
/*     */         }
/*     */       } else {
/* 136 */         this.assertReadLocks.set(Boolean.valueOf(true));
/*     */       }
/*     */     }
/* 139 */     return true;
/*     */   }
/*     */   
/*     */   private synchronized boolean debugReleaseReadLock()
/*     */   {
/* 144 */     if (this.zLock.getReadHoldCount() == 0) {
/* 145 */       this.assertReadLocks.remove();
/*     */     }
/* 147 */     return true;
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   int getQueueLength() {
/* 152 */     return this.zLock.getQueueLength();
/*     */   }
/*     */   
/*     */   @VisibleForTesting
/*     */   boolean hasQueuedThreads() {
/* 157 */     return this.zLock.hasQueuedThreads();
/*     */   }
/*     */   
/*     */   public void lock(boolean write) {
/* 161 */     write = (write) || (this.mbox.requiresWriteLock());
/* 162 */     ZimbraLog.mailbox.trace("LOCK %s", new Object[] { write ? "WRITE" : "READ" });
/* 163 */     assert (neverReadBeforeWrite(write));
/*     */     try {
/* 165 */       if (tryLock(write)) {
/* 166 */         if ((this.mbox.requiresWriteLock()) && (!isWriteLockedByCurrentThread()))
/*     */         {
/* 168 */           promote(); return;
/*     */         }
/*     */         
/* 171 */         this.lockStack.push(Boolean.valueOf(write));
/*     */         try {
/* 173 */           acquireDistributedLock(write);
/*     */         } catch (ServiceException e) {
/* 175 */           release();
/* 176 */           LockFailedException lfe = new LockFailedException("lockdb", null);
/* 177 */           lfe.logStackTrace();
/* 178 */           throw lfe;
/*     */         }
/*     */       }
/*     */       else {
/* 182 */         int queueLength = this.zLock.getQueueLength();
/* 183 */         if (queueLength >= LC.zimbra_mailbox_lock_max_waiting_threads.intValue())
/*     */         {
/*     */ 
/*     */ 
/* 187 */           LockFailedException e = new LockFailedException("too many waiters: " + queueLength, null);
/* 188 */           if (DebugConfig.debugMailboxLock) {
/* 189 */             e.logStackTrace();
/*     */           }
/* 191 */           throw e;
/*     */         }
/*     */         
/* 194 */         if (tryLockWithTimeout(write)) {
/* 195 */           if ((this.mbox.requiresWriteLock()) && (!isWriteLockedByCurrentThread()))
/*     */           {
/* 197 */             promote(); return;
/*     */           }
/*     */           
/* 200 */           this.lockStack.push(Boolean.valueOf(write));
/*     */           try {
/* 202 */             acquireDistributedLock(write);
/*     */           } catch (ServiceException e) {
/* 204 */             release();
/* 205 */             LockFailedException lfe = new LockFailedException("lockdb", null);
/* 206 */             lfe.logStackTrace();
/* 207 */             throw lfe;
/*     */           }
/*     */           return;
/*     */         }
/* 211 */         LockFailedException e = new LockFailedException("timeout", null);
/* 212 */         e.logStackTrace();
/* 213 */         throw e;
/*     */       }
/* 215 */     } catch (InterruptedException e) { throw new LockFailedException("interrupted", e, null);
/*     */     } finally {
/* 217 */       if ((!$assertionsDisabled) && (isUnlocked()) && (!debugReleaseReadLock())) throw new AssertionError();
/*     */     }
/*     */   }
/*     */   
/*     */   public void release() {
/* 222 */     Boolean write = Boolean.valueOf(false);
/*     */     try {
/* 224 */       write = (Boolean)this.lockStack.pop();
/*     */     }
/*     */     catch (EmptyStackException ese)
/*     */     {
/* 228 */       ZimbraLog.mailbox.trace("release when not locked?");
/* 229 */       assert (getHoldCount() == 0);
/* 230 */       assert (debugReleaseReadLock());
/* 231 */       return;
/*     */     }
/*     */     
/* 234 */     ZimbraLog.mailbox.trace("RELEASE %s", new Object[] { write.booleanValue() ? "WRITE" : "READ" });
/*     */     
/* 236 */     releaseDistributedLock(write.booleanValue());
/* 237 */     if (write.booleanValue()) {
/* 238 */       assert (this.zLock.getWriteHoldCount() > 0);
/* 239 */       this.zLock.writeLock().unlock();
/*     */     } else {
/* 241 */       this.zLock.readLock().unlock();
/* 242 */       assert (debugReleaseReadLock());
/*     */     }
/*     */   }
/*     */   
/*     */   private void promote() {
/* 247 */     assert (getHoldCount() == this.zLock.getReadHoldCount());
/* 248 */     int count = this.zLock.getReadHoldCount();
/* 249 */     for (int i = 0; i < count - 1; i++) {
/* 250 */       release();
/*     */     }
/* 252 */     this.zLock.readLock().unlock();
/* 253 */     assert (debugReleaseReadLock());
/* 254 */     for (int i = 0; i < count; i++) {
/* 255 */       lock(true);
/*     */     }
/*     */   }
/*     */   
/*     */   public final class LockFailedException extends RuntimeException {
/*     */     private static final long serialVersionUID = -6899718561860023270L;
/*     */     
/*     */     private LockFailedException(String message) {
/* 263 */       super();
/*     */     }
/*     */     
/*     */     private LockFailedException(String message, Throwable cause) {
/* 267 */       super(cause);
/*     */     }
/*     */     
/*     */     private void logStackTrace() {
/* 271 */       StringBuilder out = new StringBuilder("Failed to lock mailbox\n");
/* 272 */       MailboxLock.this.zLock.printStackTrace(out);
/* 273 */       ZimbraLog.mailbox.error(out, this);
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/mailbox/MailboxLock.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */